package com.zqll.gdx.starter.assets.utils;

import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.scenes.scene2d.ui.CheckBox;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.List;
import com.badlogic.gdx.scenes.scene2d.ui.SelectBox;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.TextField;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.ui.Window;
import com.badlogic.gdx.utils.ObjectMap;
import com.zqll.gdx.starter.assets.StarterAssets;

/**
 * Skin字体工具类
 * 提供便捷的方法来为LibGDX的Skin添加/替换字体，并同步更新styles
 */
public class SkinFonts {
    
    /**
     * 为Skin添加字体
     * @param skin 要添加字体的Skin对象
     * @param font 字体对象
     * @param fontName 字体名称
     */
    public static void addFont(Skin skin, BitmapFont font, String fontName) {
        if (skin == null || font == null || fontName == null) {
            throw new IllegalArgumentException("参数不能为null");
        }
        skin.add(fontName, font, BitmapFont.class);
    }
    
    /**
     * 为Skin添加默认字体
     * @param skin 要添加字体的Skin对象
     * @param fontSize 字体大小
     */
    public static void addDefaultFont(Skin skin, int fontSize) {
        StarterAssets.init();
        BitmapFont font = StarterAssets.font(fontSize);
        addFont(skin, font, "default-font");
    }
    
    /**
     * 为Skin添加指定名称的字体
     * @param skin 要添加字体的Skin对象
     * @param fontSize 字体大小
     * @param fontName 字体名称
     */
    public static void addNamedFont(Skin skin, int fontSize, String fontName) {
        StarterAssets.init();
        BitmapFont font = StarterAssets.font(fontSize);
        addFont(skin, font, fontName);
    }
    
    /**
     * 为Skin添加指定颜色的字体
     * @param skin 要添加字体的Skin对象
     * @param fontSize 字体大小
     * @param fontName 字体名称
     * @param color 字体颜色
     */
    public static void addColoredFont(Skin skin, int fontSize, String fontName, Color color) {
        StarterAssets.init();
        BitmapFont font = StarterAssets.font(fontSize, color);
        addFont(skin, font, fontName);
    }
    
    /**
     * 为Skin添加增量字体（只包含指定文本的字符）
     * @param skin 要添加字体的Skin对象
     * @param fontSize 字体大小
     * @param text 需要支持的文本内容
     * @param fontName 字体名称
     */
    public static void addIncrementalFont(Skin skin, int fontSize, String text, String fontName) {
        StarterAssets.init();
        BitmapFont font = StarterAssets.getFontGenerator().generateIncrementalFont(fontSize, text);
        addFont(skin, font, fontName);
    }
    
    /**
     * 为Skin添加多种大小的字体
     * @param skin 要添加字体的Skin对象
     * @param fontSizes 字体大小数组
     * @param baseName 基础字体名称
     */
    public static void addMultipleSizedFonts(Skin skin, int[] fontSizes, String baseName) {
        StarterAssets.init();
        for (int size : fontSizes) {
            BitmapFont font = StarterAssets.font(size);
            String fontName = baseName + "-" + size;
            addFont(skin, font, fontName);
        }
    }
    
    /**
     * 检查Skin中是否包含指定名称的字体
     * @param skin Skin对象
     * @param fontName 字体名称
     * @return 是否包含该字体
     */
    public static boolean hasFont(Skin skin, String fontName) {
        return skin.has(fontName, BitmapFont.class);
    }
    
    /**
     * 从Skin中获取字体
     * @param skin Skin对象
     * @param fontName 字体名称
     * @return 字体对象，如果不存在则返回null
     */
    public static BitmapFont getFont(Skin skin, String fontName) {
        return hasFont(skin, fontName) ? skin.get(fontName, BitmapFont.class) : null;
    }

    // ------------------------
    // 替换与同步更新 style 的字体
    // ------------------------

    /**
     * 替换Skin中的某个字体（按名称），并可选同步更新所有使用该字体的styles。
     * 常见于：皮肤已加载且styles已实例化，后续需要切换到支持中文的字体。
     *
     * @param skin Skin对象
     * @param fontName 要替换的字体资源名（如"default-font"）
     * @param newFont 新字体对象
     * @param updateStyles 是否同步更新所有style中引用的该字体
     * @param disposeOld 是否在更新后释放旧字体资源
     */
    public static void replaceFont(Skin skin, String fontName, BitmapFont newFont, boolean updateStyles, boolean disposeOld) {
        if (skin == null || fontName == null || newFont == null) {
            throw new IllegalArgumentException("参数不能为null");
        }
        BitmapFont oldFont = hasFont(skin, fontName) ? skin.get(fontName, BitmapFont.class) : null;
        // 覆盖Skin中的字体资源映射
        skin.add(fontName, newFont, BitmapFont.class);
        // 同步更新styles中的引用（旧style对象持有的是旧BitmapFont的引用，不会因add而自动变化）
        if (updateStyles) {
            updateStylesFontReferences(skin, oldFont, newFont);
        }
        // 释放旧字体（确保不再被style引用后再释放）
        if (disposeOld && oldFont != null && oldFont != newFont) {
            oldFont.dispose();
        }
    }

    /**
     * 将所有常见UI组件的style字体统一替换为指定字体（不区分之前引用）。
     * @param skin Skin对象
     * @param newFont 新字体对象
     */
    public static void replaceAllStyleFonts(Skin skin, BitmapFont newFont) {
        if (skin == null || newFont == null) {
            throw new IllegalArgumentException("参数不能为null");
        }
        updateStylesFontReferences(skin, null, newFont);
    }

    /**
     * 便捷方法：生成指定大小、支持中文的字体并替换所有style，适用于gdx-skins皮肤中文支持。
     * @param skin Skin对象
     * @param fontSize 字体大小
     */
    public static void replaceAllStyleFontsWithSize(Skin skin, int fontSize) {
        StarterAssets.init();
        BitmapFont newFont = StarterAssets.font(fontSize);
        replaceAllStyleFonts(skin, newFont);
        // 同时确保默认字体名也指向新字体（方便后续按名获取）
        skin.add("default-font", newFont, BitmapFont.class);
    }

    // 内部：同步更新各类Style中对BitmapFont的引用
    private static void updateStylesFontReferences(Skin skin, BitmapFont oldFont, BitmapFont newFont) {
        // Label
        ObjectMap<String, Label.LabelStyle> labelStyles = skin.getAll(Label.LabelStyle.class);
        if (labelStyles != null) {
            for (ObjectMap.Entry<String, Label.LabelStyle> e : labelStyles) {
                Label.LabelStyle s = e.value;
                if (s != null && (oldFont == null || s.font == oldFont)) {
                    s.font = newFont;
                }
            }
        }
        // TextButton
        ObjectMap<String, TextButton.TextButtonStyle> tbStyles = skin.getAll(TextButton.TextButtonStyle.class);
        if (tbStyles != null) {
            for (ObjectMap.Entry<String, TextButton.TextButtonStyle> e : tbStyles) {
                TextButton.TextButtonStyle s = e.value;
                if (s != null && (oldFont == null || s.font == oldFont)) {
                    s.font = newFont;
                }
            }
        }
        // TextField
        ObjectMap<String, TextField.TextFieldStyle> tfStyles = skin.getAll(TextField.TextFieldStyle.class);
        if (tfStyles != null) {
            for (ObjectMap.Entry<String, TextField.TextFieldStyle> e : tfStyles) {
                TextField.TextFieldStyle s = e.value;
                if (s != null && (oldFont == null || s.font == oldFont)) {
                    s.font = newFont;
                }
                // 同步 messageFont（JSON中可能引用同名字体，如 font-label）
                if (s != null && (oldFont == null || s.messageFont == oldFont)) {
                    s.messageFont = newFont;
                }
            }
        }
        // CheckBox
        ObjectMap<String, CheckBox.CheckBoxStyle> cbStyles = skin.getAll(CheckBox.CheckBoxStyle.class);
        if (cbStyles != null) {
            for (ObjectMap.Entry<String, CheckBox.CheckBoxStyle> e : cbStyles) {
                CheckBox.CheckBoxStyle s = e.value;
                if (s != null && (oldFont == null || s.font == oldFont)) {
                    s.font = newFont;
                }
            }
        }
        // List
        ObjectMap<String, List.ListStyle> listStyles = skin.getAll(List.ListStyle.class);
        if (listStyles != null) {
            for (ObjectMap.Entry<String, List.ListStyle> e : listStyles) {
                List.ListStyle s = e.value;
                if (s != null && (oldFont == null || s.font == oldFont)) {
                    s.font = newFont;
                }
            }
        }
        // SelectBox
        ObjectMap<String, SelectBox.SelectBoxStyle> sbStyles = skin.getAll(SelectBox.SelectBoxStyle.class);
        if (sbStyles != null) {
            for (ObjectMap.Entry<String, SelectBox.SelectBoxStyle> e : sbStyles) {
                SelectBox.SelectBoxStyle s = e.value;
                if (s != null && (oldFont == null || s.font == oldFont)) {
                    s.font = newFont;
                }
            }
        }
        // Window（标题字体）
        ObjectMap<String, Window.WindowStyle> winStyles = skin.getAll(Window.WindowStyle.class);
        if (winStyles != null) {
            for (ObjectMap.Entry<String, Window.WindowStyle> e : winStyles) {
                Window.WindowStyle s = e.value;
                if (s != null && (oldFont == null || s.titleFont == oldFont)) {
                    s.titleFont = newFont;
                }
            }
        }
        // 反射式回退：覆盖未知第三方样式中的 BitmapFont 字段
        updateStylesFontReferencesReflective(skin, oldFont, newFont);
    }

    // 反射式通用更新：适配第三方库样式（如 vis-ui）
    private static void updateStylesFontReferencesReflective(Skin skin, BitmapFont oldFont, BitmapFont newFont) {
        if (skin == null || newFont == null) return;
        try {
            java.lang.reflect.Field resField = Skin.class.getDeclaredField("resources");
            resField.setAccessible(true);
            Object resourcesObj = resField.get(skin);
            @SuppressWarnings("unchecked")
            com.badlogic.gdx.utils.ObjectMap<Class, com.badlogic.gdx.utils.ObjectMap<String, Object>> resources =
                    (com.badlogic.gdx.utils.ObjectMap<Class, com.badlogic.gdx.utils.ObjectMap<String, Object>>) resourcesObj;
            java.util.IdentityHashMap<Object, Boolean> visited = new java.util.IdentityHashMap<>();
            for (com.badlogic.gdx.utils.ObjectMap.Entry<Class, com.badlogic.gdx.utils.ObjectMap<String, Object>> ce : resources) {
                Class<?> type = ce.key;
                if (type == BitmapFont.class) continue; // 资源表中的字体已替换
                com.badlogic.gdx.utils.ObjectMap<String, Object> map = ce.value;
                if (map == null) continue;
                for (com.badlogic.gdx.utils.ObjectMap.Entry<String, Object> e : map) {
                    Object styleObj = e.value;
                    if (styleObj != null) {
                        updateObjectFontsRecursive(styleObj, oldFont, newFont, visited, 0);
                    }
                }
            }
        } catch (Throwable ignored) {
            // 反射失败时安静跳过，避免影响正常流程
        }
    }

    private static void updateObjectFontsRecursive(Object obj, BitmapFont oldFont, BitmapFont newFont,
                                                   java.util.IdentityHashMap<Object, Boolean> visited, int depth) {
        if (obj == null) return;
        if (visited.containsKey(obj)) return;
        visited.put(obj, Boolean.TRUE);
        Class<?> clazz = obj.getClass();
        while (clazz != Object.class) {
            java.lang.reflect.Field[] fields = clazz.getDeclaredFields();
            for (java.lang.reflect.Field f : fields) {
                int mod = f.getModifiers();
                if (java.lang.reflect.Modifier.isStatic(mod)) continue;
                f.setAccessible(true);
                try {
                    Object val = f.get(obj);
                    if (val == null) continue;
                    Class<?> t = f.getType();
                    if (BitmapFont.class.isAssignableFrom(t)) {
                        if (oldFont == null || val == oldFont) {
                            f.set(obj, newFont);
                        }
                    } else if (t.isArray()) {
                        Class<?> ct = t.getComponentType();
                        if (BitmapFont.class.isAssignableFrom(ct)) {
                            Object[] arr = (Object[]) val;
                            for (int i = 0; i < arr.length; i++) {
                                if (oldFont == null || arr[i] == oldFont) {
                                    arr[i] = newFont;
                                }
                            }
                        } else {
                            Object[] arr = (Object[]) val;
                            for (Object item : arr) {
                                updateObjectFontsRecursive(item, oldFont, newFont, visited, depth + 1);
                            }
                        }
                    } else if (val instanceof com.badlogic.gdx.utils.Array) {
                        com.badlogic.gdx.utils.Array array = (com.badlogic.gdx.utils.Array) val;
                        for (int i = 0; i < array.size; i++) {
                            Object item = array.get(i);
                            if (item instanceof BitmapFont) {
                                if (oldFont == null || item == oldFont) {
                                    array.set(i, newFont);
                                }
                            } else {
                                updateObjectFontsRecursive(item, oldFont, newFont, visited, depth + 1);
                            }
                        }
                    } else if (val instanceof java.util.List) {
                        java.util.List list = (java.util.List) val;
                        for (int i = 0; i < list.size(); i++) {
                            Object item = list.get(i);
                            if (item instanceof BitmapFont) {
                                if (oldFont == null || item == oldFont) {
                                    list.set(i, newFont);
                                }
                            } else {
                                updateObjectFontsRecursive(item, oldFont, newFont, visited, depth + 1);
                            }
                        }
                    } else if (!t.isPrimitive() && !t.getName().startsWith("java.") && !t.getName().startsWith("javax.")) {
                        updateObjectFontsRecursive(val, oldFont, newFont, visited, depth + 1);
                    }
                } catch (Throwable ignored) {
                }
            }
            clazz = clazz.getSuperclass();
        }
    }
}